package com.irdstudio.efp.esb.service.impl.sed.cfca;

import com.alibaba.fastjson.JSONObject;
import com.irdstudio.basic.framework.core.exception.BizException;
import com.irdstudio.basic.framework.core.exception.ESBException;
import com.irdstudio.basic.framework.core.util.Base64;
import com.irdstudio.basic.framework.core.util.FileUtil;
import com.irdstudio.basic.framework.core.util.UUIDUtil;
import com.irdstudio.efp.console.common.ConsoleConstant;
import com.irdstudio.efp.esb.common.client.resp.EsbRespRetInfBean;
import com.irdstudio.efp.esb.common.client.resp.EsbRespServiceBean;
import com.irdstudio.efp.esb.common.constant.ElectronicSignatureConstant;
import com.irdstudio.efp.esb.service.bo.req.dzqz.ReqElectronicSignature3203Bean;
import com.irdstudio.efp.esb.service.bo.req.dzqz.ReqElectronicSignatureDL01Bean;
import com.irdstudio.efp.esb.service.bo.req.sed.cfca.ReqElectronicSignature3207Bean;
import com.irdstudio.efp.esb.service.bo.req.sed.cfca.ReqSedCfcaOpenAccountBean;
import com.irdstudio.efp.esb.service.bo.resp.dzqz.RspElectronicSignature3203Bean;
import com.irdstudio.efp.esb.service.bo.resp.dzqz.RspElectronicSignatureDL01Bean;
import com.irdstudio.efp.esb.service.bo.resp.sed.cfca.RespElectronicSignature3207Bean;
import com.irdstudio.efp.esb.service.bo.resp.sed.cfca.RespSedCfcaOpenAccountBean;
import com.irdstudio.efp.esb.service.client.ESBClientFactory;
import com.irdstudio.efp.esb.service.facade.sed.cfca.SedCfcaService;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* 普税贷产品对税e贷产品的此部分代码做了复用
* 说明：为了保证功能的复用性、提升开发的工作效率，对该代码进行了复用
* 评估是否会影响线上功能：否
* 评估时间：2020/5/25
* 处理方案：保留复用
*/
/**
 * 税E贷合同签订-cfca服务（开户、签章、下载）<br/>
 * @author  zhoucs1
 * @version 1.0
 * @since   2019年4月22日 下午6:03:00
 */
@Service("sedCfcaService")
public class SedCfcaServiceImpl implements SedCfcaService{

	private static Logger logger = LoggerFactory.getLogger(SedCfcaServiceImpl.class);
	
	//授信合同模板文件路径
	@Value("${contract.sxModelFilePath}")
	private String contractSxModelFilePath;
	
	//贷款合同模板文件路径
	@Value("${contract.dkModelFilePath}")
	private String contractDkModelFilePath;
	
	@Value("${contract.tmpFilePath}")
	private String contractTmpFilePath;
	
	//合同模板变量名集合
	private final static List<String> contractList;
	static {
		contractList = new ArrayList<String>();
		
		//两种合同共有变量名
		contractList.add("lmtContNo");      //授信合同号
		contractList.add("orgName");        //银行全称
		contractList.add("orgAddr");        //机构地址
		contractList.add("orgZipcode");     //机构邮编
		contractList.add("orgTel");         //机构电话
		contractList.add("cusName");        //客户姓名
		contractList.add("certType");       //证件类型
		contractList.add("certCode");       //证件号码
		contractList.add("cusAddr");        //客户住址
		contractList.add("cusZipcode");     //客户邮编
		contractList.add("cusPhone");       //客户电话
		contractList.add("cusTax");         //客户传真
		contractList.add("cusEmail");       //客户电子邮箱
		contractList.add("signDate");       //签约日期 yyyy-MM-dd
		contractList.add("certCode2");      //身份证号
		                                    
		//授信合同特有变量名                
		contractList.add("lmtAmtUpper");    //授信额度大写
		contractList.add("lmtAmtLower");    //授信额度小写
		contractList.add("validMonth");     //授信有效期，转换成月
		contractList.add("lmtStartYear");   //授信开始日期，取年
		contractList.add("lmtStartMonth");  //授信开始日期，取月
		contractList.add("lmtStartDay");    //授信开始日期，取日
		contractList.add("lmtEndYear");     //授信结束日期，取年
		contractList.add("lmtEndMonth");    //授信结束日期，取月
		contractList.add("lmtEndDay");      //授信结束日期，取日
		                                    
		//贷款合同特有变量名                
		contractList.add("contNo");         //合同编号
		contractList.add("distAccName");    //贷款发放账户名
		contractList.add("distAccountNo");  //贷款发放账号
		contractList.add("repayAccountNo"); //还款账户名（银行）
		contractList.add("repayAccName");   //还款账号（银行）
		contractList.add("loanAmtUpper");   //贷款金额大写
		contractList.add("loanAmtLower");   //贷款金额小写
		contractList.add("loanTermType");   //贷款期限类型（转换成1,2,3）
		contractList.add("loanTermTypeY");  //贷款期限按年计（多少年）
		contractList.add("loanTermTypeM");  //贷款期限按月计（多少月）
		contractList.add("loanTermTypeO");  //其他（文字说明）
		contractList.add("finRateType");    //利率浮动类型（转换成1,2,3）
		contractList.add("finUp");          //利率上浮比例（finUp%）
		contractList.add("finDown");        //利率下调比例（finDown%）
		contractList.add("repayMode");      //还款方式（转换成1,2,3）
		contractList.add("repayDay1");      //还款方式1的还款日
		contractList.add("repayDay2");      //还款方式2的还款日
		contractList.add("repayDay3");      //还款方式3的还款日
	}
	
	/**
	 * 开户<br/>
	 * @author zhoucs1
	 * @since  2019年4月22日 下午5:53:58
	 * @param  req
	 * @return
	 */
	@Override
	public RespSedCfcaOpenAccountBean openAccount(ReqSedCfcaOpenAccountBean req) throws ESBException{
		logger.info("电子签章处理个人开户请求参数：" + JSONObject.toJSONString(req));
		// 连接ESB请求电子签章个人开户接口
		RespSedCfcaOpenAccountBean rspBean = new RespSedCfcaOpenAccountBean();// 个人开户响应信息
		try {
			// 接收个人开户响应信息
			EsbRespServiceBean esbRespServiceBean = ESBClientFactory.buildClient()
					.withBody(req)
					.withTradeNo(ElectronicSignatureConstant.TRADE_NO_20120001)
					.withScene(ElectronicSignatureConstant.SCENE_04)
					.create()
					.sendAndReceive();
			
			Optional.ofNullable(esbRespServiceBean.getSysHead()).orElseThrow(()->new ESBException("访问ESB发生异常，请检查请求参数是否正确或ESB是否连通"));
			
			//处理开放平台返回信息
			List<EsbRespRetInfBean> retInfArry = esbRespServiceBean.getSysHead().getRetInfArry();
			Optional.ofNullable(retInfArry).orElseThrow(()->new ESBException("开放平台返回的处理信息数组RetInfArry为空"));
			if(!"000000".equals(retInfArry.get(0).getRetCd())) {
				throw new  ESBException("开放平台发生异常，返回信息为" + retInfArry.toString());
			}
					
			rspBean = esbRespServiceBean.getBody(RespSedCfcaOpenAccountBean.class);
			
			
			logger.info("电子签章处理个人开户结束，返回信息：" + JSONObject.toJSONString(rspBean));
		} catch (ESBException e) {
			e.printStackTrace();
			logger.error("电子签章处理个人开户出现异常：" + e.getMessage());
			throw new ESBException("电子签章处理个人开户出现异常：" + e.getMessage());
		}
		return rspBean;
	}
	/**
	 * 上传合同签署<br/>
	 * @author zhoucs1
	 * @since  2019年4月22日 下午5:53:58
	 * @param reqSedCfcaBean
	 * @return
	 */
	@Override
	public RspElectronicSignature3203Bean signContract(ReqElectronicSignature3203Bean req3203Bean)
			throws ESBException {
		logger.info("电子签章处理上传合同签署请求参数：" + JSONObject.toJSONString(req3203Bean));
		// 连接ESB请求电子签章上传合同签署接口
		RspElectronicSignature3203Bean rspBean = new RspElectronicSignature3203Bean();// 上传合同签署响应信息
		try {
			// 接收上传合同签署响应信息
			EsbRespServiceBean esbRespServiceBean = ESBClientFactory.buildClient().withBody(req3203Bean)
					.withTradeNo(ElectronicSignatureConstant.TRADE_NO_30220008)
					.withScene(ElectronicSignatureConstant.SCENE_01).create()
					.sendAndReceive();

			Optional.ofNullable(esbRespServiceBean.getSysHead()).orElseThrow(()->new ESBException("访问ESB发生异常，请检查请求参数是否正确或ESB是否连通"));
			
			//处理开放平台返回信息
			List<EsbRespRetInfBean> retInfArry = esbRespServiceBean.getSysHead().getRetInfArry();
			Optional.ofNullable(retInfArry).orElseThrow(()->new ESBException("开放平台返回的处理信息数组RetInfArry为空"));
			if(!"000000".equals(retInfArry.get(0).getRetCd())) {
				throw new  ESBException("开放平台发生异常，返回信息为" + retInfArry.toString());
			}
			
			rspBean = esbRespServiceBean.getBody(RspElectronicSignature3203Bean.class);
			
			logger.info("电子签章处理：上传合同签署结束，返回信息：" + JSONObject.toJSONString(rspBean));
		} catch (ESBException e) {
			logger.error("电子签章处理上传合同签署出现异常：" + e.getMessage());
			throw new ESBException("电子签章处理上传合同签署出现异常：" + e.getMessage());
		} catch (Exception e) {
			logger.error("电子签章处理上传合同签署出现异常：" + e.getMessage());
			throw new ESBException("电子签章处理上传合同签署出现异常：" + e.getMessage());
		}
		return rspBean;
	}

	/**
	 * 关键字签署（可用于单独签署印章）<br/>
	 * @author zhoucs1
	 * @since  2019年4月22日 下午5:53:58
	 * @param reqSedCfcaBean
	 * @return 
	 */
	@Override
	public RespElectronicSignature3207Bean signSeal(ReqElectronicSignature3207Bean req3207Bean) throws ESBException {
		logger.info("电子签章处理关键字签署，请求参数：" + JSONObject.toJSONString(req3207Bean));
		// 连接ESB请求电子签章关键字签署接口
		RespElectronicSignature3207Bean resp3207Bean = new RespElectronicSignature3207Bean();// 关键字签署响应信息
		try {
			// 接收关键字签署响应信息
			EsbRespServiceBean esbRespServiceBean = ESBClientFactory.buildClient()
					.withBody(req3207Bean)
					.withTradeNo(ElectronicSignatureConstant.TRADE_NO_30220008)
					.withScene(ElectronicSignatureConstant.SCENE_04)
					.create()
					.sendAndReceive();
			Optional.ofNullable(esbRespServiceBean.getSysHead()).orElseThrow(()->new ESBException("访问ESB发生异常，请检查请求参数是否正确或ESB是否连通"));
			
			//处理开放平台返回信息
			List<EsbRespRetInfBean> retInfArry = esbRespServiceBean.getSysHead().getRetInfArry();
			Optional.ofNullable(retInfArry).orElseThrow(()->new ESBException("开放平台返回的处理信息数组RetInfArry为空"));
			if(!"000000".equals(retInfArry.get(0).getRetCd())) {
				throw new  ESBException("开放平台发生异常，返回信息为" + retInfArry.toString());
			}
		
			resp3207Bean = esbRespServiceBean.getBody(RespElectronicSignature3207Bean.class);
			
			
			logger.info("电子签章处理关键字签署结束，返回信息：" + JSONObject.toJSONString(resp3207Bean));
			
		} catch (ESBException e) {
			logger.error("电子签章处理关键字签署出现异常：" + e.getMessage());
			throw new ESBException("电子签章处理关键字签署出现异常：" + e.getMessage());
		}
		return resp3207Bean;
	}
	/**
	 * 下载合同<br/>
	 * @author zhoucs1
	 * @since  2019年4月22日 下午5:53:58
	 * @param  reqCADL01Bean
	 * @return
	 */
	@Override
	public RspElectronicSignatureDL01Bean downloadContract(ReqElectronicSignatureDL01Bean reqCADL01Bean) throws ESBException{
		logger.debug("电子签章处理：合同下载请求参数：" + JSONObject.toJSONString(reqCADL01Bean));
		// 连接ESB请求电子签章合同下载接口
		RspElectronicSignatureDL01Bean rspBean = new RspElectronicSignatureDL01Bean();// 合同下载响应信息
		try {
			// 接收上传合同签署响应信息
			EsbRespServiceBean esbRespServiceBean = ESBClientFactory.buildClient()
					.withBody(reqCADL01Bean)
					.withTradeNo(ElectronicSignatureConstant.TRADE_NO_30220003)
					.withScene(ElectronicSignatureConstant.SCENE_02)
					.create()
					.sendAndReceive();
			Optional.ofNullable(esbRespServiceBean.getSysHead()).orElseThrow(()->new ESBException("访问ESB发生异常，请检查请求参数是否正确或ESB是否连通"));
			
			//处理开放平台返回信息
			List<EsbRespRetInfBean> retInfArry = esbRespServiceBean.getSysHead().getRetInfArry();
			Optional.ofNullable(retInfArry).orElseThrow(()->new ESBException("开放平台返回的处理信息数组RetInfArry为空"));
			if(!"000000".equals(retInfArry.get(0).getRetCd())) {
				throw new  ESBException("开放平台发生异常，返回信息为" + retInfArry.toString());
			}
			
			rspBean = esbRespServiceBean.getBody(RspElectronicSignatureDL01Bean.class);
			
			logger.info("电子签章处理：合同下载结束，返回信息：" + JSONObject.toJSONString(rspBean));
		} catch (ESBException e) {
			logger.error("电子签章处理合同下载出现异常：" + e.getMessage());
			throw new ESBException("电子签章处理合同下载出现异常：" + e.getMessage());
		} catch (Exception e) {
			logger.error("电子签章处理合同下载出现异常：" + e.getMessage());
			throw new ESBException("电子签章处理合同下载出现异常：" + e.getMessage());
		}
		return rspBean;
	}
	@Override
	public String createLocalContract(String contractType,Map<String,String> contractMap) throws Exception{
		logger.info("正在读取本地合同模板文件");
		PdfReader pdfReader = null;
		FileOutputStream out = null;
		PdfStamper pdfStamper = null;
		try {
			//获取合同模板文件
			if(ConsoleConstant.CONT_TYPE_01.equals(contractType)) {
				pdfReader = new PdfReader(contractSxModelFilePath);
			} else if(ConsoleConstant.CONT_TYPE_02.equals(contractType)) {
				pdfReader = new PdfReader(contractDkModelFilePath);
			}
			
			//获取生成的合同放置的文件
			File outFile = new File(contractTmpFilePath);
			out = new FileOutputStream(outFile);
			
			//获取表单操作工具
			pdfStamper = new PdfStamper(pdfReader,out);
			
			//新建支持中文的字体
			BaseFont baseFont = BaseFont.createFont("STSongStd-Light","UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
			
			//获取表单中的变量
			AcroFields fields = pdfStamper.getAcroFields();
			
			//设置变量的内容生成后不可更改
			pdfStamper.setFormFlattening(true);
			
			//如果合同字段映射map为空，抛出异常
			if(contractMap == null) {
				throw new BizException("合同字段映射contractMap不能为空");
			}
			logger.info("开始在本地生成合同文件，临时合同文件地址为：" + contractTmpFilePath);
			//设置变量中的字体,并设置变量中的值
			for (String key : contractMap.keySet()) {
				fields.setFieldProperty(key, "textFont", baseFont, null);
				fields.setField(key, contractMap.get(key));
			}
			//关闭操作流
			if(pdfStamper != null) {
				pdfStamper.close();
			}
			if(out != null) {
				out.close();
			}
			if(pdfReader != null) {
				pdfReader.close();
			}
			outFile = new File(contractTmpFilePath);
			//返回本地生成的合同文件的二进制流
			return Base64.encodeBytes(FileUtil.toByteArray(outFile));
			
		} catch (BizException e) {
			throw new BizException(e);
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("请检查合同模板是否在配置的路径下");
		} finally { //关闭占用的资源文件
			if(pdfStamper != null) {
				pdfStamper.close();
			}
			if(out != null) {
				out.close();
			}
			if(pdfReader != null) {
				pdfReader.close();
			}
		}
	}

    @Override
    public String fillPdfContent(String contractType, Map<String, String> contractMap) throws Exception {
        String pdfFilePath = null;
        if (ConsoleConstant.CONT_TYPE_01.equals(contractType)) {
            pdfFilePath = contractSxModelFilePath;
        } else if (ConsoleConstant.CONT_TYPE_02.equals(contractType)) {
            pdfFilePath = contractDkModelFilePath;
        }
        if (StringUtils.isEmpty(pdfFilePath)) {
            throw new Exception("非法的合同类型");
        }
        if (!new File(pdfFilePath).exists()) {
            throw new Exception("根据pdf路径获取到的模板文件不存在");
        }
        if (contractMap == null || contractMap.size() < 1) {
            throw new Exception("填充内容不能都为空");
        }
        logger.info("填充模板内容开始：");
        PdfReader pdfReader = null;
        FileOutputStream out = null;
        PdfStamper pdfStamper = null;
        File tmpFile = null;
        try {
            //获取合同模板文件
            pdfReader = new PdfReader(pdfFilePath);
            String tmpFileName = FilenameUtils.getFullPath(pdfFilePath) + UUIDUtil.getUUID() + ".pdf";
            //获取生成的合同放置的文件
            out = new FileOutputStream(tmpFileName);
            //获取表单操作工具
            pdfStamper = new PdfStamper(pdfReader, out);

            //新建支持中文的字体
            BaseFont baseFont = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

            //获取表单中的变量
            AcroFields fields = pdfStamper.getAcroFields();

            //设置变量的内容生成后不可更改
            pdfStamper.setFormFlattening(true);
            //设置变量中的字体,并设置变量中的值
            contractMap.forEach((k, v) -> {
                fields.setFieldProperty(k, "textFont", baseFont, null);
                try {
                    fields.setField(k, v);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error("填充内容报错", e);
                }
            });
            pdfStamper.flush();
            if (pdfStamper != null) {
                pdfStamper.close();
            }
            if (out != null) {
                out.close();
            }
            if (pdfReader != null) {
                pdfReader.close();
            }
            tmpFile = new File(tmpFileName);
            if (!tmpFile.exists()) {
                throw new RuntimeException("获取临时文件出错");
            }
            String rtBase64Str = Base64.encodeBytes(FileUtil.toByteArray(tmpFile));
            if (StringUtils.isEmpty(rtBase64Str)) {
                throw new RuntimeException("填充模板文件出错，生成的文件为空");
            }
            //返回本地生成的合同文件的二进制流
            return rtBase64Str;
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("请检查合同模板是否在配置的路径下");
        } finally { //关闭占用的资源文件
            if (pdfStamper != null) {
                pdfStamper.close();
            }
            if (out != null) {
                out.close();
            }
            if (pdfReader != null) {
                pdfReader.close();
            }
            if (tmpFile != null) {
                tmpFile.delete();
            }
        }
    }
}
